import simplejson
import optparse
import logging
import io_utilities
import datetime
from suds.client import Client
import Queue
import threading
import urllib2
import constants


class ThreadIssue(threading.Thread):
  def __init__(self,reading_queue,writing_queue,jira):
    threading.Thread.__init__(self)
    self.reading_queue = reading_queue
    self.writing_queue = writing_queue
    self.jira = jira
  
  def get_remote_comments(self, issue_key, trial_number):
    r_comments = []
    try:
      r_comments = self.jira._get_remote_comments(issue_key)
    except urllib2.URLError as err:
      if (trial_number < constants.MAX_TRIALS):
        logging.info('''Timeout when getting comments for issue %s, 
                        trying again...''', 
            issue_key)
        self.get_remote_comments(issue_key,trial_number+1)
      else:
        logging.error('''Cannot get comments for issue %s, 
                        due to timeouts skipping them...''',
            issue_key)
    except Exception as err:
      logging.error('Cannot get comments for issue %s, skipping them...', 
            issue_key)
    return r_comments  
  
  def run(self):
    while True:
      r_issue = self.reading_queue.get()
      r_comments = self.get_remote_comments(r_issue['key'],0)
      self.writing_queue.put(Issue(r_issue,r_comments))
      if  (self.writing_queue.qsize() % 100 == 0):
        logging.info('Retrieved comments for at least %s issues' % 
            self.writing_queue.qsize())
      self.reading_queue.task_done()
  

def remote_field_to_string(remote_field):
  if not remote_field:
    return ''
  if isinstance(remote_field,datetime.datetime):
    return str(remote_field)
  
  return remote_field.encode('UTF-8')


class Issue(dict):
  def __init__(self, remote_issue, remote_comments=None):
    self['comments'] = []   
    
    for key in constants.RELEVANT_ISSUE_DETAILS:
      try:
        self[key] = remote_field_to_string(
          remote_issue[key])
      except AttributeError as err:
        logging.error('Issue %s does not have key %s using empty value', 
          (remote_issue['key'],key))
        self[key] = ''
    if remote_comments:
      self.add_comments(remote_comments)
  
      
  def add_comments(self,remote_comments):
    self['comments'] = [Comment(r_comment) 
        for r_comment in remote_comments]
  
  
class Comment(dict):
  def __init__(self, remote_comment):
    for key in constants.RELEVANT_COMMENT_DETAILS:
      try:  
        self[key] = remote_field_to_string(remote_comment[key]) 
      except AttributeError: 
        self[key] = ""  



class Jira(object):
  def __init__(self, wsdl, username, password, system, query_limit):
    logging.info('Getting client from wsdl %s ' % wsdl)
    self.client = Client(wsdl)
    logging.info('Getting auth for user %s ' % username)
    self.auth = self.client.service.login(username, password)
    self.system = system
    self.query_limit = query_limit
  
  def logout(self):
    return self.client.service.logout(self.auth)
  
  def _get_query(self,filters):
    jql_query = self._get_system_query()
    for each_filter in filters:
      jql_query = jql_query + ' and ' + each_filter
    return jql_query
  
  def _get_system_query(self):
    return 'project="%s"' % (self.system)
  
  def _get_latest_remote_issue(self):
    jql_query = self._get_query([])
    issues = self.client.service.getIssuesFromJqlSearch(self.auth,jql_query,1)
    logging.info('Detected [%s] as last issue id' % issues[0]['key'])
    return issues[0]
  
  def get_latest_issue(self):
    return Issue(self._get_latest_remote_issue())
  
  def get_issue_with_comments(self, issue_key):
    key_query = 'key="%s"' % issue_key
    jql_query = self._get_query([key_query])
    issues = self.client.service.getIssuesFromJqlSearch(self.auth,
                    jql_query,1)
    if not issues:
      return None
    else:
      return Issue(issues[0],self._get_remote_comments(issues[0]['key']))
  
  def _get_remote_comments(self, issue_key):
    return self.client.service.getComments(self.auth,issue_key)
  
  
  def _get_remote_issues_from(self,issue_key):
    last_key = issue_key
    all_r_issues = []
    while True:
      logging.info('Getting maximum %s issues backwards from %s' % 
          (self.query_limit,last_key))
      key_query = 'key<"%s"' % last_key
      jql_query = self._get_query([key_query])
      issues = self.client.service.getIssuesFromJqlSearch(
                self.auth, jql_query, self.query_limit)
      all_r_issues.extend(issues)
      #we check whether we are done
      if (not issues) or (len(issues) < self.query_limit):
        break
      last_key = issues[-1]['key']
    logging.info('Retrieved %s issues, no comments yet' % len(all_r_issues))
    return all_r_issues
  
  def _get_issues_with_comments_from_remote(self,remote_issues):
    reading_queue = Queue.Queue()
    writing_queue = Queue.Queue()
    
    for thread in xrange(constants.NUM_OF_THREADS):
      t = ThreadIssue(reading_queue,writing_queue,self)
      t.setDaemon(True)
      t.start()
    
    for remote_issue in remote_issues:
      reading_queue.put(remote_issue)
    
    reading_queue.join()    
    all_issues = {}
    while writing_queue.qsize() > 0:
      issue = writing_queue.get()
      all_issues[issue['key']] = issue
    return all_issues
  
  
  def get_issues_with_comments(self):
    all_r_issues = []
    latest_r_issue = self._get_latest_remote_issue()
    all_r_issues.append(latest_r_issue)
    all_r_issues.extend(self._get_remote_issues_from(latest_r_issue['key']))
    
    return self._get_issues_with_comments_from_remote(all_r_issues)


def get_opts():
  parser = optparse.OptionParser()
  parser.add_option('-d', '--debug', dest='debug', 
                    default=False,
                    action='store_true', help='Enable debugging.')
  parser.add_option('-u', '--username', dest='username', 
                    default='sback',
                    help='Username to access JIRA web service.')
  parser.add_option('-p', '--password', dest='password', 
                    default='piripicchio',
                    help='Password to access JIRA web service')
  parser.add_option('-w', '--wsdl', dest='wsdl', 
  default='https://issues.apache.org/jira/rpc/soap/jirasoapservice-v2?wsdl',
                    help='WSDL to access JIRA web service')
  parser.add_option('-s', '--system', dest='system', 
                    default='trb',
                    help='Software system to download issues from')
  parser.add_option('-l', '--limit', dest='limit', 
                    default=1000,
                    help='Maximum number of issues per query')
  parser.add_option('-j', '--json', dest='json',
                    default=None,
                    help='File for writing the results in JSON format')
    
  opts, args = parser.parse_args()
  if len(args) != 0:
    parser.error('Wrong arguments')
  
  logging.basicConfig()
  if opts.debug:
    logging.getLogger().setLevel(logging.DEBUG)
  else:
    logging.getLogger().setLevel(logging.INFO)
   
  return opts



def fetch_reports(system_name,
      max_issues_per_query=1000,
      wsdl='https://issues.apache.org/jira/rpc/soap/jirasoapservice-v2?wsdl',
      username='sback', password='piripicchio'):
  
  jira = Jira(wsdl,username,password,system_name,max_issues_per_query)
  all_issues = jira.get_issues_with_comments()
  jira.logout()
  return all_issues


def main():
  opts = get_opts()
  all_issues = fetch_reports(opts.system, opts.limit, opts.wsdl,
                        opts.username,opts.password)
  if opts.json:
    io_utilities.write_json(all_issues,opts.json,indent=2,sort_keys=True)


if __name__ == '__main__':
  main()